package org.kjs.common.clone.implement;

import org.kjs.common.clone.AccessibleFieldStructure;
import org.kjs.common.clone.Clone;
import org.kjs.common.clone.exception.CloneNotSupportedException;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

/**
 * Created by IntelliJ IDEA.
 * User: KJS
 * Date: 11-8-15
 * Time: 下午7:16
 * To change this template use File | Settings | File Templates.
 */
public class AccessibleField<V> implements AccessibleFieldStructure<V> {

    private String name;
    private Method accessibleFieldSetMethod;
    private Method accessibleFieldGetMethod;

    private Class<V> cloneableItemClass;

    AccessibleField (Class<V> cloneableItemClass) {
        this.cloneableItemClass = cloneableItemClass;
    }

    public void cloneAccessibleField(V originalItem, V clonedItem, Clone clone) throws CloneNotSupportedException {
        if (isAvailableAccessibleField()) {
            Object [] parameters = null;

            parameters = new Object [] {};
            try {
                accessibleFieldGetMethod.setAccessible(true);
                Object value = accessibleFieldGetMethod.invoke(originalItem, parameters);
                accessibleFieldGetMethod.setAccessible(false);

                parameters = new Object [] {value};

                accessibleFieldSetMethod.setAccessible(true);
                accessibleFieldSetMethod.invoke(clonedItem, parameters);
                accessibleFieldSetMethod.setAccessible(false);
            } catch (IllegalAccessException e) {
                throw new CloneNotSupportedException("IllegalAccessException : " + e.getMessage());
            } catch (InvocationTargetException e) {
                throw new CloneNotSupportedException("InvocationTargetException : " + e.getMessage());
            }
        }
    }

    protected String getAccessibleFieldMethodName(Method method) {
        String methodName = null;

        if (null != method) {
            methodName = method.getName();
            if (methodName.startsWith(ACCESSIBLE_FIELD_METHOD_SET)) {
                methodName = methodName.substring(ACCESSIBLE_FIELD_METHOD_SET.length());
            } else if (methodName.startsWith(ACCESSIBLE_FIELD_METHOD_GET)) {
                methodName = methodName.substring(ACCESSIBLE_FIELD_METHOD_GET.length());
            } else if (methodName.startsWith(ACCESSIBLE_FIELD_METHOD_IS)) {
                methodName = methodName.substring(ACCESSIBLE_FIELD_METHOD_IS.length());
            } else {
                methodName = null;
            }
        }

        return methodName;
    }

    public boolean isAvailableAccessibleField() {
        boolean isAvailable = false;

        if (null != cloneableItemClass) {
            if (null != accessibleFieldSetMethod && null != accessibleFieldGetMethod) {
                if (null != name) {
                    if (null != accessibleFieldSetMethod && null != accessibleFieldGetMethod) {
                        if (accessibleFieldSetMethod.getDeclaringClass() ==
                                accessibleFieldGetMethod.getDeclaringClass()) {
                            String setMethodName = getAccessibleFieldMethodName(accessibleFieldSetMethod);
                            String getMethodName = getAccessibleFieldMethodName(accessibleFieldGetMethod);
                            if (null != setMethodName && null != getMethodName &&
                                    name.equals(setMethodName) && setMethodName.equals(getMethodName)) {
                                isAvailable = true;
                            }
                        }
                    }
                }
            }
        }

        return isAvailable;
    }

    public String getName() {
        return name;
    }

    protected void setName(String name) {
        this.name = name;
    }

    public Method getAccessibleFieldSetMethod() {
        return accessibleFieldSetMethod;
    }

    protected boolean checkAccessibleFieldMethod (Method accessibleFieldMethod) {
        boolean isAvailableAccessFieldMethod = false;

        if (null != accessibleFieldGetMethod && null != accessibleFieldSetMethod) {
            isAvailableAccessFieldMethod = true;
        }

        return isAvailableAccessFieldMethod;
    }

    protected void setAccessibleFieldSetMethod(Method accessibleFieldSetMethod) {
        this.accessibleFieldSetMethod = null;

        if (checkAccessibleFieldMethod(accessibleFieldSetMethod)) {
            if (accessibleFieldSetMethod.getParameterTypes().length == 1) {
                this.accessibleFieldSetMethod = accessibleFieldSetMethod;
            }
        }
    }

    public Method getAccessibleFieldGetMethod() {
        return accessibleFieldGetMethod;
    }

    protected void setAccessibleFieldGetMethod(Method accessibleFieldGetMethod) {
        this.accessibleFieldGetMethod = null;

        if (checkAccessibleFieldMethod(accessibleFieldGetMethod)) {
            if (accessibleFieldGetMethod.getParameterTypes().length == 0) {
                this.accessibleFieldGetMethod = accessibleFieldGetMethod;
            }
        }
    }

    public Class<V> getCloneableItemClass() {
        return cloneableItemClass;
    }

    protected void setCloneableItemClass(Class<V> cloneableItemClass) {
        this.cloneableItemClass = cloneableItemClass;
    }
}
